﻿#define _CRT_SECURE_NO_WARNINGS
#include"snack.h"
int score = 0;  
int add = 1;
snake_t* head = NULL;//蛇头指针
snake_t* food = NULL;//食物指针
int direction = RIGHT;//移动方向
int sleepTime = 250;//帧率（ms）
int endStatus = 0;//失败状态 1撞墙 2咬自己 3Esc

int setColor(int c) {
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
	return 0;
}
int gotoXY(int x, int y) {
	COORD c;
	c.X = x, c.Y = y;

	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
	return 0;
}
int ShowSnack() {		
	system("cls");//清屏

	gotoXY(35, 1);
	setColor(6);
	printf("/^\\/^\\");      //蛇眼

	gotoXY(34, 2);
	printf("|__|  0");      //蛇眼

	gotoXY(33, 2);
	setColor(2);
	printf("_");

	gotoXY(25, 3);
	setColor(12);
	printf("\\/");        //蛇信

	gotoXY(31, 3);
	setColor(2);
	printf("/");

	gotoXY(37, 3);
	setColor(6);
	printf("\\_/");       //蛇眼

	gotoXY(41, 3);
	setColor(10);
	printf("\\");

	gotoXY(26, 4);
	setColor(12);
	printf("\\____");     //舌头

	gotoXY(32, 4);
	printf("_________/");

	gotoXY(31, 4);
	setColor(2);
	printf("|");

	gotoXY(42, 4);
	setColor(10);
	printf("\\");

	gotoXY(31, 5);
	setColor(2);
	printf("\\_______");     //蛇嘴

	gotoXY(43, 5);
	setColor(10);
	printf("\\");

	gotoXY(38, 6);
	printf(" |    |                               \\");
	//下面都是画蛇身

	gotoXY(38, 7);
	printf(" /    /                               \\ \\");

	gotoXY(37, 8);
	printf(" /    /                                \\  \\");

	gotoXY(36, 9);
	printf(" /    /                                  \\  \\");

	gotoXY(35, 10);
	printf(" /    /                                    \\  \\");

	gotoXY(34, 11);
	printf(" /    /                _----_                \\  \\");

	gotoXY(33, 12);
	printf(" /    /             _--~       --_            |   |");

	gotoXY(33, 13);
	printf("(    (        _----~     _--_      --_      _/    |");

	gotoXY(34, 14);
	printf("\\    ~-——-~     --~---    ~--      ~-_-~       /");

	gotoXY(35, 15);
	printf("\\           -—~              ~—-         -—~");

	gotoXY(36, 16);
	printf("~—-----—~                      ~—---—~        ");

	return 0;
}
int Menu() {
	showCursor();
	int i, j,n;
	ShowSnack();//蛇图形

	setColor(11);//输出标题
	gotoXY(49, 18);
	printf("贪吃蛇游戏\n");

	
	setColor(14);//打印游戏边框
	for (i = 20; i <= 26; i++) {             
		for (j = 27; j <= 74; j++){
			
			gotoXY(j, i);
			if (i == 20 || i == 26) {
				printf("-");
			}
			else if (j == 27 || j == 74) {
				printf("|");
			}
			else {
				printf(" ");
			}
		}
	}

	setColor(12);//输出菜单
	gotoXY(35, 22);
	printf("1. 开始游戏");
	gotoXY(55, 22);
	printf("2. 游戏说明");
	gotoXY(35, 24);
	printf("3. 退出游戏");
	gotoXY(27, 27);
	printf("请选择[1 2 3]:");

	scanf("%d", &n);//获取输入
	getchar();
	return n;
	
}
int ExplainationGame() {
	system("cls");
	
	setColor(13);
	gotoXY(44, 3);
	printf("游戏说明");

	gotoXY(30, 8);
	setColor(3);
	printf("★ 1.不能撞到墙，也不能咬到自己");

	gotoXY(30, 11);
	setColor(5);
	printf("★ 2.F1 或 蛇吃到食物 加速前进， F2减速前进");

	gotoXY(30, 14);
	setColor(11);
	printf("★ 3.使用空格暂停或继续游戏");

	gotoXY(30, 17);
	setColor(13);
	printf("★ 4.使用↑↓→←控制前进方向");

	gotoXY(30, 20);
	setColor(14);
	printf("★ 5.按下Esc键盘退出游戏");

	gotoXY(20, 24);
	setColor(12);
	printf("请按下回车键返回主界面");

	return 0;
}
int PrintMap(){
	hideCursor();
	
	int i, j;
	system("cls");
	
	for (i = 0; i <= 26; i++) {
		for (j = 0; j <= 56; j += 2) {
			gotoXY(j, i);
			if (i == 0 || i == 26 || j == 0 || j == 56) {
				setColor(5);
				printf("□");
			}
			else {
				setColor(3);
				printf("■");
			}
		}
	}
	return 0;
}
int ShowScore(){
	hideCursor();

	//显示最高分
	int highScore = 0;
	gotoXY(64, 4);
	setColor(3);
	highScore = readFile();
	printf("最高分记录：%d", highScore);

	//显示得分
	gotoXY(64, 8);
	setColor(14);
	printf("得分：%d", score);

	return 0;
}
int readFile(){
	int n = 0;
	FILE* fp = NULL;
	fp = fopen("data.txt", "r");//只读
	
	if (fp == NULL) {
		// 文件不存在，创建并写入默认值
		fp = fopen("data.txt", "w");
		if (fp == NULL) {
			printf("无法创建 data.txt 文件！\n");
			return 0;  // 返回默认值 0
		}
		fprintf(fp, "0");  // 写入默认分数 0
		fclose(fp);
		return 0;
	}

	fscanf(fp, "%d", &n);
	fclose(fp);
	return n;
}
int writeFile(){
	FILE* fp = NULL;
	fp = fopen("data.txt", "w");
	if (fp == NULL) {
		printf("Open fail...\n");
		return 1;
	}

	fprintf(fp, "%d", score);
	fclose(fp);
	return 0;
}
int initSnake(){
	
	int i;
	head = NULL;
	snake_t *temp = NULL;
	for (i = 0; i < 4; i++) {//创建4个蛇节点
		temp = NULL;
		temp = (snake_t*)malloc(sizeof(snake_t));
		if (temp == NULL) {
			printf("malloc failed...\n");
			break;
		}
		//初始化行列
		temp->x = 24 + i * 2;
		temp->y = 5;

		//头插法
		temp->next = head;
		head = temp;
	}

	//打印蛇
	while (temp != NULL) {
		setColor(14);
		gotoXY(temp->x, temp->y);

		if (temp == head) {//蛇头
			printf("●");
		}
		else {//蛇身
			printf("★");
		}
		temp = temp->next;
	}

	return 0;
}
int randFood()
{
	food = NULL;
	snake_t* p = NULL;
	snake_t* tmp_head = NULL;

	p = (snake_t*)malloc(sizeof(snake_t));
	if (p == NULL) {
		printf("malloc failed...\n");
		return -1;
	}
	while (1) {//直到成功创建食物
		memset(p, 0, sizeof(snake_t));
		srand(time(NULL));
		//处理X坐标
		while (1) {
			p->x = rand() % 53 + 2;
			if (p->x %2 ==0) break;
		}
		
		//处理y坐标
		p->y = rand() % 25 + 1;

		//判断蛇神和食物是否重叠
		while (tmp_head != NULL) {
			if (tmp_head->x == p->x&&tmp_head->y&&p->y) break;
			tmp_head = tmp_head->next;
		}

		//不重合
		if (tmp_head == NULL) {
			food = p;

			gotoXY(p->x, p->y);
			setColor(12);
			printf("●");
			break;
		}
		else continue;
	}

	return 0;
}
int moveSnake()//
{
	snake_t* p = NULL;
	p = (snake_t*)malloc(sizeof(snake_t));
	if (p == NULL) {
		printf("malloc failed...\n");
		return 1;
	}
	memset(p, 0, sizeof(snake_t));

	switch (direction) {
	case 1:
		p->x = head->x;
		p->y = head->y - 1;
		break;
	case 2:
		p->x = head->x;
		p->y = head->y + 1;
		break;
	case 3:
		p->x = head->x - 2;
		p->y = head->y;
		break;
	case 4:
		p->x = head->x + 2;
		p->y = head->y;
		break;
	}

	//头插新蛇头
	snake_t* tmp = head;
	p->next = head;
	head = p;

	//更改蛇图案
	setColor(14);
	gotoXY(tmp->x, tmp->y);
	printf("★");//原蛇头改为蛇身
	gotoXY(head->x, head->y);
	printf("●");//打印新蛇头
	
	//移动到食物
	if (p->x == food->x && p->y == food->y){
			//增加得分
			score += add;

			//更新食物
			randFood();

			//加速
			speedUp();
	}
	//普通移动
	else {
		//补全地图，删除尾节点
		tmp = head;
		snake_t* prew = NULL;
		while (tmp->next != NULL) { //初始蛇为4节点，不溢出
			prew = tmp;
			tmp = tmp->next;
		}
		
		gotoXY(tmp->x, tmp->y);
		setColor(3);
		
		if(tmp->x !=food->x || tmp->y != food->y)
			printf("■");
		free(tmp);
		tmp = NULL;
		prew->next = NULL;
		prew = NULL;
	}
	return 0;
}
int moveKeyControl()
{
	direction = RIGHT;
	while (1) {
		ShowScore();//循环实时分数

		//获取按键信息
		if (GetAsyncKeyState(VK_UP) && direction != DOWN) {
			direction = UP;
		}
		if (GetAsyncKeyState(VK_DOWN ) && direction != UP) {
			direction = DOWN;
		}
		if (GetAsyncKeyState(VK_LEFT) && direction != RIGHT) {
			direction = LEFT;
		}
		if (GetAsyncKeyState(VK_RIGHT) && direction != LEFT) {
			direction = RIGHT;
		}
		if (GetAsyncKeyState(VK_SPACE)) {//空格键暂停
			while (1) {
				Sleep(200);//暂停200ms
				if (GetAsyncKeyState(VK_SPACE)) {
					break;//再次按下空格键
				}
			}
		}
		//加速
		if (GetAsyncKeyState(VK_F1))
			speedUp();
		
		//减速
		if(GetAsyncKeyState(VK_F2))
			speedDown();
		//按下Esc结束游戏
		if (GetAsyncKeyState(VK_ESCAPE)) {
			endStatus = 3;
			endGame();
			break;
		}

		moveSnake();
		
		//判断是否撞墙
		if (isHitWall()) {
			endStatus = 1;
			endGame();
			break;
		}
		//判断是否撞到蛇身
		if (isBitSelf()) {
			endStatus = 2;
			endGame();
			break;
		}
		Sleep(sleepTime);//通过sleepTime控制帧率
	}
	return 0;
}
int speedUp()
{
	if(sleepTime >50)
	{
		add += 1;//食物积分增加
		sleepTime -= 10;//加速10ms
	}
	return 0;
}
int speedDown()
{
	if (add > 1) {
		add  -= 1; //食物积分减少
		sleepTime += 10;//减速10ms
	}
	return 0;
}
int isHitWall()//判断是1否0撞墙
{
	if (head->x == 0 || head->x == 56 || head->y == 0 || head->y == 26) {
		return 1;
	}
	return 0;
}
int isBitSelf()//判断是1否0咬到自己
{
	snake_t* tmp = NULL;
	tmp = head->next;
	while (tmp != NULL) {
		if (head->x == tmp->x && head->y == tmp->y) {
			return 1;
		}
		tmp = tmp->next;
	}
	return 0;
}
int failGameUi(){
	showCursor();
	
	int i;
	//清屏
	system("cls");

	//显示标题
	gotoXY(44, 3);
	setColor(12);
	printf("游 戏 失 败！！！");

	//输出上下边框
	gotoXY(17, 5);
	setColor(3);
	printf("+----------------------------------------------------------------+");

	gotoXY(17, 20);
		printf("+----------------------------------------------------------------+");

	for (i = 6; i < 20; i++){
			gotoXY(17, i);
			printf("|");

			gotoXY(82, i);
			printf("|");
	}

	return 0;
}
int endGame()
{
	int highScore = 0;

	failGameUi();
	switch (endStatus) {
	case 1:
		printf("您撞到墙壁，游戏结束！！！！\n");
		break;
	case 2:
		printf("您咬到自己，游戏结束！！！\n");
		break;
	case 3:
		printf("您已结束游戏\n");
		break;
	default:
		break;
	}
	
	gotoXY(43, 12);
	setColor(13);
	printf("您的得分:%d", score);

	highScore = readFile();

	//显示是否刷记录
	gotoXY(38, 16);
	setColor(10);
	if (highScore > score)
	{
		printf("加油喔，离最高分还差%d分", highScore - score);
	}
	else
	{
		printf("恭喜你，你已经创记录了!!!");
		//将最高分写入文件里面
		writeFile();
	}

	//显示菜单
	gotoXY(25, 23);
	setColor(12);
	printf("再玩一局请输入：1");

	gotoXY(52, 23);
	printf("直接退出请输入：2");

	gotoXY(46, 25);
	setColor(11);
	printf("请选择：");


	int n;
	
	do{
		scanf("%d", &n);
		getchar();

		if(n == 1){
			score = 0;
			add = 1;
			sleepTime = 250;
			FreeSnake();

			break;
		}
		else if (n == 2)	exit(0);
		else{
			setColor(12);
			gotoXY(30, 27);
			printf("您的输入有误，请重新输入，按下任意键继续");
			getchar();
			continue;
		}
	}while (1);

	return 0;
}
int FreeSnake()
{
	snake_t* tmp = head;
	while (tmp != NULL) {
		snake_t* p = tmp;
		tmp = tmp->next;
		free(p);
	}
	head = NULL;
	if (food != NULL) free(food);
	food = NULL;
	return 0;
}
void hideCursor() {
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cursorInfo;

	GetConsoleCursorInfo(hOut, &cursorInfo);
	cursorInfo.bVisible = FALSE;  // 设置光标不可见
	SetConsoleCursorInfo(hOut, &cursorInfo);
}
void showCursor() {
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cursorInfo;

	GetConsoleCursorInfo(hOut, &cursorInfo);
	cursorInfo.bVisible = TRUE;  // 设置光标可见
	SetConsoleCursorInfo(hOut, &cursorInfo);
}



